package com.example.zyl.common.query.aspect;

import com.example.zyl.common.CommonResult;
import com.example.zyl.common.query.annotation.Sensitive;
import com.example.zyl.common.query.enums.SensitiveMapEnum;
import com.example.zyl.common.query.enums.SensitiveStrategy;
import com.example.zyl.common.utils.ToolUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Stream;

@Slf4j
@Aspect
@Component
public class SensitiveAspect {

    @AfterReturning(value = "@annotation(com.example.zyl.common.query.annotation.ApiSensitive)", returning = "result")
    public Object sensitiveData(Object result) {

        try {
            if (result != null) {
                this.handleClassType(result);
            }
        } catch (Exception e) {
            log.error("脱敏数据处理失败 ======> {}", e.getMessage(), e);
        }

        return result;
    }


    /**
     * 校验对象类型
     *
     * @param result 待校验对象
     * @throws Exception 异常
     */
    private void handleClassType(Object result) throws Exception {

        // 非对象类型直接返回
        if (ToolUtils.isObjectType(result)) return;

        // 处理集合，进行循环处理
        if (result instanceof Collection) {
            Collection<?> collection = (Collection<?>) result;
            for (Object object : collection) {
                this.handleSensitiveField(object);
            }
            return;
        }
        // 处理CommonResult
        if (result instanceof CommonResult) {
            CommonResult<?> commonResult = (CommonResult<?>) result;
            if (commonResult.isSuccess()) {
                this.handleClassType(commonResult.getData());
            }
            return;
        }
        this.handleSensitiveField(result);
    }


    /**
     * 检查返回值类中的字段，如果有带有 @Sensitive 注解的字段，进行脱敏处理
     *
     * @param result 待处理对象
     * @throws Exception 异常
     */
    private void handleSensitiveField(Object result) throws Exception {

        if (ToolUtils.isObjectType(result)) return;

        if (result instanceof Map) {
            Map<Object, Object> map = (Map<Object, Object>) result;
            SensitiveMapEnum[] values = SensitiveMapEnum.values();
            map.forEach((key, valueObj) -> {
                SensitiveMapEnum mapEnum = Stream.of(values)
                        .filter(filter -> ToolUtils.containsIgnoreCase(key.toString(), filter.getKey()))
                        .findFirst().orElseGet(() -> {
                            try {
                                this.handleClassType(valueObj);
                            } catch (Exception e) {
                                return null;
                            }
                            return null;
                        });

                if (mapEnum == null || valueObj == null) return;

                if (valueObj instanceof String) {
                    String apply = mapEnum.getSensitive().desensitizer().apply((String) valueObj);
                    map.put(key, apply);
                }
            });
            return;
        }

        if (result instanceof Collection || result instanceof CommonResult) {
            this.handleClassType(result);
            return;
        }

        //获取目标方法属性
        Field[] fields = result.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Sensitive.class)) {
                if (field.getType() == String.class) {
                    String value = (String) field.get(result);
                    SensitiveStrategy strategy = field.getAnnotation(Sensitive.class).strategy();
                    String apply = strategy.desensitizer().apply(value);
                    field.set(result, apply);
                }
            } else {
                Object fieldValue = field.get(result);
                if (fieldValue == null) continue;
                this.handleClassType(fieldValue);
            }
        }
    }
}
